/*
 * Copyright (c) 2011, Michael van der Westhuizen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    - Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    - Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef PYXMLSEC1_BASE64_C_MCVDW_20110104
#define PYXMLSEC1_BASE64_C_MCVDW_20110104

#include "pyxmlsec.h"

#include <xmlsec/base64.h>



static PyObject * xmlsec1_xmlSecBase64CtxCreate(PyObject * self, PyObject * args, PyObject * kwds)
{
    xmlSecBase64CtxPtr ptr;
    PyObject * obj;
    int encode;
    int columns;

    static char * kwlist[] = {"encode", "columns", NULL};

    ptr = NULL;
    obj = NULL;
    encode = 0;
    columns = xmlSecBase64GetDefaultLineSize();

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwlist, &encode, &columns))
    {
        return NULL;
    }

    if (NULL == (ptr = xmlSecBase64CtxCreate(encode, columns)))
    {
        ENSURE_EXCEPTION_SET(xmlSecBase64CtxCreate);
        return NULL;
    }

    if (NULL == (obj = xmlsec1mod_wrap_xmlSecBase64CtxPtr(ptr)))
    {
        ENSURE_EXCEPTION_SET(xmlsec1mod_wrap_xmlSecBase64CtxPtr);
        xmlSecBase64CtxDestroy(ptr);
        return NULL;
    }

    return obj;
}



static PyObject * xmlsec1_xmlSecBase64CtxInitialize(PyObject * self, PyObject * args, PyObject * kwds)
{
    xmlSecBase64CtxPtr ptr;
    PyObject * obj;
    int encode;
    int columns;

    static char * kwlist[] = {"ctx", "encode", "columns", NULL};

    ptr = NULL;
    obj = NULL;
    encode = 0;
    columns = xmlSecBase64GetDefaultLineSize();

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi|i", kwlist, &obj, &encode, &columns))
    {
        return NULL;
    }

    if (NULL == (ptr = xmlsec1mod_unwrap_xmlSecBase64CtxPtr(obj)))
    {
        return NULL;
    }

    xmlSecBase64CtxFinalize(ptr);

    if (0 != xmlSecBase64CtxInitialize(ptr, encode, columns))
    {
        ENSURE_EXCEPTION_SET(xmlSecBase64CtxInitialize);
        return NULL;
    }

    Py_RETURN_NONE;
}



static PyObject * xmlsec1_xmlSecBase64CtxDestroy(PyObject * self, PyObject * obj)
{
    xmlSecBase64CtxPtr ptr = NULL;

    if (NULL == (ptr = xmlsec1mod_unwrap_xmlSecBase64CtxPtr(obj)))
    {
        return NULL;
    }

    xmlSecBase64CtxDestroy(ptr);
    Py_RETURN_NONE;
}



static PyObject * xmlsec1_xmlSecBase64CtxFinalize(PyObject * self, PyObject * obj)
{
    xmlSecBase64CtxPtr ptr = NULL;

    if (NULL == (ptr = xmlsec1mod_unwrap_xmlSecBase64CtxPtr(obj)))
    {
        return NULL;
    }

    xmlSecBase64CtxFinalize(ptr);
    Py_RETURN_NONE;
}



static PyObject * xmlsec1_xmlSecBase64SetDefaultLineSize(PyObject * self, PyObject * args, PyObject * kwds)
{
    int columns;
    static char * kwlist[] = {"columns", NULL};

    columns = XMLSEC_BASE64_LINESIZE;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &columns))
    {
        return NULL;
    }

    xmlSecBase64SetDefaultLineSize(columns);
    Py_RETURN_NONE;
}



static PyObject * xmlsec1_xmlSecBase64GetDefaultLineSize(PyObject * self, PyObject * arg)
{
    return PyInt_FromLong((long)xmlSecBase64GetDefaultLineSize());
}



static PyObject * xmlsec1_xmlSecBase64CtxUpdate(PyObject * self, PyObject * args, PyObject * kwds)
{
    xmlSecBase64CtxPtr ptr;
    PyObject * obj1;
    PyObject * obj2;
    xmlSecByte * in;
    xmlSecSize inSize;
    xmlSecByte * out;
    xmlSecSize outSize;
    int outLen;
    PyObject * ret;

    static char * kwlist[] = {"ctx", "in", NULL};

    ptr = NULL;
    obj1 = obj2 = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &obj1, &obj2))
    {
        return NULL;
    }

    if (NULL == (ptr = xmlsec1mod_unwrap_xmlSecBase64CtxPtr(obj1)))
    {
        return NULL;
    }

    if (0 != xmlsec1_ByteArrayOrBytesToXmlSecBytes(obj2, &in, &inSize))
    {
        ENSURE_EXCEPTION_SET(xmlsec1_ByteArrayOrBytesToXmlSecBytes);
        return NULL;
    }

    outSize = inSize * 4;

    if (NULL == (out = PyMem_New(xmlSecByte, (int)outSize)))
    {
        PyMem_Del(in);
        return PyErr_NoMemory();
    }

    outLen = xmlSecBase64CtxUpdate(ptr, in, inSize, out, outSize);
    PyMem_Del(in);
    in = NULL;
    inSize = 0;

    if (-1 == outLen)
    {
        PyMem_Del(out);
        ENSURE_EXCEPTION_SET(xmlSecBase64CtxUpdate);
        return NULL;
    }

    ret = PyByteArray_FromStringAndSize((char *)out, (Py_ssize_t)outLen);
    PyMem_Del(out);
    return ret;
}



static PyObject * xmlsec1_xmlSecBase64CtxFinal(PyObject * self, PyObject * arg)
{
    xmlSecBase64CtxPtr ptr;
    xmlSecByte * out;
    xmlSecSize outSize;
    int outLen;
    PyObject * ret;

    if (NULL == (ptr = xmlsec1mod_unwrap_xmlSecBase64CtxPtr(arg)))
    {
        return NULL;
    }

    outSize = XMLSEC_BASE64_LINESIZE * 4;

    if (NULL == (out = PyMem_New(xmlSecByte, (int)outSize)))
    {
        return PyErr_NoMemory();
    }

    if (-1 == (outLen = xmlSecBase64CtxFinal(ptr, out, outSize)))
    {
        PyMem_Del(out);
        ENSURE_EXCEPTION_SET(xmlSecBase64CtxFinal);
        return NULL;
    }

    ret = PyByteArray_FromStringAndSize((char *)out, (Py_ssize_t)outLen);
    PyMem_Del(out);
    return ret;
}



static PyObject * xmlsec1_xmlSecBase64Encode(PyObject * self, PyObject * args, PyObject * kwds)
{
    PyObject * obj;
    int columns;
    xmlSecByte * in;
    xmlSecSize inSize;
    xmlChar * res;
    PyObject * ret;

    static char * kwlist[] = {"buf", "columns", NULL};

    obj = NULL;
    columns = xmlSecBase64GetDefaultLineSize();
    in = NULL;
    inSize = 0;
    res = NULL;
    ret = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, &obj, &columns))
    {
        return NULL;
    }

    if (0 != xmlsec1_ByteArrayOrBytesToXmlSecBytes(obj, &in, &inSize))
    {
        ENSURE_EXCEPTION_SET(xmlsec1_ByteArrayOrBytesToXmlSecBytes);
        return NULL;
    }

    if (NULL == (res = xmlSecBase64Encode(in, inSize, columns)))
    {
        ENSURE_EXCEPTION_SET(xmlSecBase64Encode);
        PyMem_Del(in);
        return NULL;
    }

    ret = PyString_FromString((char *)res);
    PyMem_Del(res);
    return ret;
}



static PyObject * xmlsec1_xmlSecBase64Decode(PyObject * self, PyObject * in)
{
    xmlChar * str;
    xmlSecSize len;
    xmlSecByte * buf;
    int out_len;
    PyObject * tmp;
    PyObject * retval;

    str = NULL;
    len = 0;
    buf = NULL;
    out_len = 0;
    tmp = retval = NULL;

    if (PyBytes_Check(in))
    {
        str = xmlCharStrdup(PyBytes_AsString(in));
    }
    else if (PyUnicode_Check(in))
    {
        if (NULL == (tmp = PyUnicode_AsUTF8String(in)))
        {
            return NULL;
        }

        str = xmlCharStrdup(PyString_AsString(tmp));
        Py_CLEAR(tmp);
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "input must be a string (bytes) or unicode object");
        return NULL;
    }

    if (NULL == str)
    {
        PyErr_SetString(PyExc_TypeError, "failed to extract char array from input object");
        return NULL;
    }

    len = (xmlSecSize)xmlStrlen(str);

    if (NULL == (buf = PyMem_New(xmlSecByte, (int)len)))
    {
        xmlFree(str);
        return PyErr_NoMemory();
    }

    if (-1 == (out_len = xmlSecBase64Decode(str, buf, len)))
    {
        ENSURE_EXCEPTION_SET(xmlSecBase64Decode);
        xmlFree(str);
        PyMem_Del(buf);
        return NULL;
    }

    xmlFree(str);
    retval = PyByteArray_FromStringAndSize((char *)buf, (Py_ssize_t)out_len);
    PyMem_Del(buf);
    return retval;
}

#endif
